package com.xyoto.takeawayBusiness.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import com.xyoto.takeawayBusiness.constant.WechatConstant;
import com.xyoto.takeawayBusiness.entity.Orders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

/**
 * 微信支付实现类
 *
 * @author Administrator
 */
@Component
@Service
public class WechatPayUtils implements WechatPay {

    private static X509Certificate WECHAT_PAY_CERTIFICATES;
    /**
     * 商户号
     */
    private static final String MCH_ID = "";
    /**
     * 商户API证书的证书序列号
     */
    private static final String MERCHANT_SERIAL_NUMBER = "";
    /**
     * 同一下单接口
     */
    private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    /**
     * 查询订单接口
     */
    private static final String ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/";
    /**
     * 订单关闭接口
     */
    private static final String ORDER_CLOSE_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no";
    /**
     * 订单退款接口
     */
    private static final String ORDER_REFUND_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    /**
     * API3KEY
     */
    private static final String APP_V3_KEY = "";

    /**
     * 商品描述
     */
    private static String DESCRIPTION = "";
    /**
     * 通知地址
     */
    private static final String NOTIFY_URL = "";

    /**
     * 商户私钥
     */
    private static PrivateKey PRIVATE_KEY;

//    static {
//        try {
//            PRIVATE_KEY = PemUtil.loadPrivateKey(new FileInputStream("/path/to/apiclient_key.pem"));
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * 单例实现微信支付链接
     */
    public static class WechatPayClientHolder {
        private static CloseableHttpClient httpClient;

        static {
            try {
                CertificatesManager certificatesManager = CertificatesManager.getInstance();
                // 向证书管理器增加需要自动更新平台证书的商户信息
                certificatesManager.putMerchant(MCH_ID, new WechatPay2Credentials(MCH_ID,
                        new PrivateKeySigner(MERCHANT_SERIAL_NUMBER, PRIVATE_KEY)), APP_V3_KEY.getBytes(StandardCharsets.UTF_8));
                // ... 若有多个商户号，可继续调用putMerchant添加商户信息
                // 从证书管理器中获取verifier
                Verifier verifier = certificatesManager.getVerifier(MCH_ID);
                WECHAT_PAY_CERTIFICATES = verifier.getValidCertificate();
                WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                        .withMerchant(MCH_ID, MERCHANT_SERIAL_NUMBER, PRIVATE_KEY)
                        .withValidator(new WechatPay2Validator(verifier));
                // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient

                // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
                httpClient = builder.build();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 获取微信支付链接
     *
     * @return
     */
    private static CloseableHttpClient getHttpClient() {
        return WechatPayClientHolder.httpClient;
    }


    /**
     * 实现使用JSAPI生成预订单
     *
     * @param orders 订单实体
     * @param openid
     * @return
     */
    @Override
    public String advanceOrderByJSAPI(Orders orders, String openid) throws Exception {

        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(UNIFIED_ORDER_URL);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("appid", WechatConstant.APP_ID)
                .put("mch_id", MCH_ID)
                .put("description", DESCRIPTION)
                .put("notify_url", NOTIFY_URL)
                .put("out_trade_no", orders.getId());
        rootNode.putObject("amount").put("total", orders.getPayerTotal());
        rootNode.putObject("payer").put("openid", openid);
        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        return EntityUtils.toString(response.getEntity());
    }

    /**
     * 实现根据订单id查询订单
     *
     * @param orderId 订单id
     * @return
     */
    @Override
    public String getOrderStatusByJSAPI(String orderId) throws Exception {

        CloseableHttpClient httpClient = getHttpClient();
        URIBuilder uriBuilder = new URIBuilder(ORDER_QUERY_URL + orderId + "?" + MCH_ID);
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader("Accept", "application/json");

        CloseableHttpResponse response = httpClient.execute(httpGet);

        return EntityUtils.toString(response.getEntity());
    }

    /**
     * 根据订单id关闭订单
     *
     * @param orderId 订单id
     * @return
     */
    @Override
    public String closeOrderByJSAPI(String orderId) throws Exception {

        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(ORDER_CLOSE_URL + orderId + "/close");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", MCH_ID);

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        return EntityUtils.toString(response.getEntity());
    }


    /**
     * 通过订单实体提交微信退款
     *
     * @param orders 订单实体
     * @return
     */
    @Override
    public String refundOrderByJSAPI(Orders orders) throws IOException {

        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(ORDER_REFUND_URL);

        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("out_trade_no", orders.getId())
                .put("out_refund_no", orders.getId())
                .put("notify_url",NOTIFY_URL);
        rootNode.putObject("amount").put("refund",orders.getPayerTotal())
                .put("total",orders.getTotal());

        objectMapper.writeValue(bos, rootNode);
        CloseableHttpResponse response = httpClient.execute(httpPost);
        return EntityUtils.toString(response.getEntity());
    }
    /**
     * 通过公钥进行加密
     *
     * @param text 需要加密的文本
     * @return
     */
    @Override
    public String encryptForPrivate(String text) {

        try {
            return RsaCryptoUtil.encryptOAEP(text, WECHAT_PAY_CERTIFICATES);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 通过私钥进行解密
     *
     * @param text 需要解密的文本
     * @return
     */
    @Override
    public String decryptForPublic(String text) {

        try {
            return RsaCryptoUtil.decryptOAEP(text, PRIVATE_KEY);
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return null;
    }
}
